/* The ns package provides the net-score.org functionality.  */
package ns

import (
	"appengine"
	"appengine/user"
	"fmt"
	"net/http"
	"strings"
	"text/template"
	"time"
)

var baseConfig = map[string]string{
	"datastoreSetPath":       "'/upload'",
	"datastoreSetErrorPath":  "'/upload/JsError'",
	"trackSetTrackingCode":   "'UA-16051354-8'",
	"trackSetRemoteAddr":     "''",
	"trackSetReferer":        "''",
	"trackSetDevKey":         "''",
	"pageSetLoggedInAdmin":   "false",
	"pageSetLoggedIn":        "false",
	"pageSetLoginUrl":        "''",
	"pageSetLogoutUrl":       "''",
	"pageSetResultsUrl":      "'/user/results/'",
	"pageSetResultDetailUrl": "'/detail/'",
	"pageSetAdminResultsUrl": "''",
	"pageSetDeveloperUrl":    "'/user/developer/'",
	"pageSetUserName":        "''",
}

var eggConfig = map[string]string{
	"lateSetServer":          "'i1.ytimg.com'",
	"lateSetResource":        "'/i/augF6Vv2TDgBRvKVeSqyUg/1.jpg'",
	"lateSetMaxPings":        "100",
	"lateSetMinPings":        "10",
	"lateSetTimeLimit":       "5000.0",
	"thrSetServer":           "'i1.ytimg.com'",
	"thrSetResource":         "'/i/pF_Dv5_lbIaAimjzSqUzWA/1.jpg'",
	"thrSetMaxPings":         "100",
	"thrSetMinPings":         "10",
	"thrSetTimeLimit":        "5000.0",
	"pageSetResultDetailUrl": "'http://www.net-score.org/user/detail/'",
	"trackSetTestForm":       "'Egg'",
}

var featherConfig = map[string]string{
	"lateSetServer":    "'i1.ytimg.com'",
	"lateSetResource":  "'/i/augF6Vv2TDgBRvKVeSqyUg/1.jpg'",
	"lateSetMaxPings":  "100",
	"lateSetMinPings":  "10",
	"lateSetTimeLimit": "3000.0",
	"thrSetServer":     "'i1.ytimg.com'",
	"thrSetResource":   "'/i/pF_Dv5_lbIaAimjzSqUzWA/1.jpg'",
	"thrSetMaxPings":   "100",
	"thrSetMinPings":   "10",
	"thrSetTimeLimit":  "3000.0",
	"trackSetTestForm": "'Feather'",
}

var flockConfig = map[string]string{
	"lateSetServer":    "'i1.ytimg.com'",
	"lateSetResource":  "'/i/augF6Vv2TDgBRvKVeSqyUg/1.jpg'",
	"lateSetMaxPings":  "100",
	"lateSetMinPings":  "10",
	"lateSetTimeLimit": "10000.0",
	"thrSetServer":     "'i1.ytimg.com'",
	"thrSetResource":   "'/i/pF_Dv5_lbIaAimjzSqUzWA/1.jpg'",
	"thrSetMaxPings":   "100",
	"thrSetMinPings":   "10",
	"thrSetTimeLimit":  "10000.0",
	"trackSetDevKey":   "'ns'",
	"trackSetTestForm": "'Flock'",
}

var resultDetailConfig = map[string]string{
	"datastoreSetResultKey": "''",
	"trackSetDevKey":        "'ns'",
	"resultdetailSetData":   "{}",
}

var resultListConfig = map[string]string{
	"trackSetDevKey":    "'ns'",
	"resultlistSetData": "{}",
}

func newConfig(tempConfig map[string]string, c appengine.Context,
	r *http.Request, gt int64) (map[string]string, error) {
	config := make(map[string]string, len(tempConfig)+len(baseConfig))
	for k, v := range baseConfig {
		config[k] = v
	}
	for k, v := range tempConfig {
		config[k] = v
	}
	u := user.Current(c)
	if u != nil {
		//TODO(gavaletz) put this in a taskqueue
		StoreUser(c, u)
		if user.IsAdmin(c) {
			config["pageSetLoggedInAdmin"] = "true"
			config["pageSetAdminResultsUrl"] = "'/ns/results/'"
			config["pageSetResultDetailUrl"] = "'/ns/detail/'"
		} else {
			config["pageSetLoggedInAdmin"] = "false"
			config["pageSetAdminResultsUrl"] = "''"
		}
		config["pageSetLoggedIn"] = "true"
		config["pageSetUserName"] = fmt.Sprintf("'%s'", u.String())
	} else {
		config["pageSetLoggedIn"] = "false"
		config["pageSetLoggedIn"] = "false"
		config["pageSetUserName"] = "''"
		config["pageSetLoggedInAdmin"] = "false"
		config["pageSetAdminResultsUrl"] = "''"
	}
	if loginUrl, err := user.LoginURL(c, r.URL.Path); err == nil {
		config["pageSetLoginUrl"] = fmt.Sprintf("'%s'", loginUrl)
	} else {
		c.Errorf("INDEX user.LoginURL err = %v", err)
		return config, err
	}
	if logoutUrl, err := user.LogoutURL(c, r.URL.Path); err == nil {
		config["pageSetLogoutUrl"] = fmt.Sprintf("'%s'", logoutUrl)
	} else {
		c.Errorf("INDEX user.LogoutURL err = %v", err)
		return config, err
	}

	config["trackSetRemoteAddr"] = fmt.Sprintf("'%s'", r.RemoteAddr)
	config["trackSetReferer"] = fmt.Sprintf("'%s'", r.Referer())
	config["trackSetRequestTime"] = fmt.Sprintf("%v", gt)
	return config, nil
}

func init() {
	http.HandleFunc("/", flock)
	http.HandleFunc("/flock", flockStart)
	http.HandleFunc("/feather/", feather)
	http.HandleFunc("/feather", feather)
	http.HandleFunc("/egg/", egg)
	http.HandleFunc("/egg", egg)
	http.HandleFunc("/ns/logout", logout)
	http.HandleFunc("/user/logout", logout)
	http.HandleFunc("/ns/login", login)
	http.HandleFunc("/user/login", login)
	staticSet.Parse(headHTML)
	staticSet.Parse(pageHTML)

	//This works because we are working on a locally hosted system.
	if appengine.IsDevAppServer() {
		eggConfig["pageSetResultDetailUrl"] = "'/user/detail/'"
	}
}

type HTMLHeader struct {
	Title      string
	ScriptList []string
	StyleList  []string
	JsConfig   map[string]string
}

type HTMLPage struct {
	Head   HTMLHeader
	OnLoad string
}

func VersionFile(path string, fileName string, c appengine.Context) string {
	v := appengine.VersionID(c)
	sv := strings.Split(v, ".")[0]
	return fmt.Sprintf("%s%s.%s", path, sv, fileName)
}

const headHTML = `{{define "header"}}<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head><title>{{.Title}}</title><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /><meta name="keywords" content="broadband,performance,network,Internet,measurement" /><meta name="description" content="a tool for measuring and analyzing broadband performance" />{{range .ScriptList}}<script type="text/javascript" src="{{html .}}"></script>{{end}}{{range .StyleList}}<link rel="stylesheet" href="{{html .}}" />{{end}}<script type="text/javascript">{{range $k, $v := .JsConfig}}{{printf "%s(%s);" $k $v}}{{end}}</script></head>{{end}}`

const pageHTML = `{{define "page"}}{{template "header" .Head}}<body onload="{{js .OnLoad}}"><noscript>To use net-score you need to allow JavaScript to run.</noscript></body></html>{{end}}`

var staticSet = new(template.Template)

func logout(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u == nil {
		//already logged out send them home
		http.Redirect(w, r, "/", 307)
		return
	}
	//log them out and then home
	logoutUrl, err := user.LogoutURL(c, "/")
	if err != nil {
		c.Errorf("logout:user.LogoutURL err = %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, logoutUrl, 307)
}

func login(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	u := user.Current(c)
	if u != nil {
		//already logged in send them home
		http.Redirect(w, r, "/", 307)
		return
	}
	//send them to the login page and then home
	loginUrl, err := user.LoginURL(c, "/")
	if err != nil {
		c.Errorf("login:user.LoginURL err = %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, loginUrl, 307)
}

func flockStart(w http.ResponseWriter, r *http.Request) {
	gt := time.Now().Unix()
	c := appengine.NewContext(r)

	//Create a TestRecord and save/cache it.
	tr := NewTestRecord(c, r, gt)
	var err error
	tr.TestForm, err = GetTestFormat("Flock")
	if err != nil {
		c.Errorf("flock:GetTestFormat err = %v", err)
	}
	tr.DevKey = "ns"
	tr.Cache(c)
	fmt.Fprintf(w, "%d", gt)
}

/*
This does not store a test recorod because we would be spammed with too many.
Instead we use the flockStart method to actually tell us when a test has
started.
*/
func flock(w http.ResponseWriter, r *http.Request) {
	gt := time.Now().Unix()
	c := appengine.NewContext(r)
	fc, err := newConfig(flockConfig, c, r, gt)
	if err != nil {
		c.Errorf("flock:newConfig err = %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	} else {
		h := HTMLHeader{
			Title: "net-score",
			ScriptList: []string{"https://www.google.com/jsapi",
				VersionFile("/", "strings-en-comp.js", c),
				VersionFile("/", "flock-comp.js", c)},
			StyleList: []string{VersionFile("/", "flock-comp.css",
				c)},
			JsConfig: fc,
		}
		p := &HTMLPage{
			Head:   h,
			OnLoad: "netscoreStart();",
		}
		err = staticSet.ExecuteTemplate(w, "page", p)
		if err != nil {
			c.Errorf("INDEX staticSet.ExecuteTemplate err = %v",
				err)
			http.Error(w, err.Error(),
				http.StatusInternalServerError)
			return
		}
	}
}

func feather(w http.ResponseWriter, r *http.Request) {
	gt := time.Now().Unix()
	c := appengine.NewContext(r)
	fc, err := newConfig(featherConfig, c, r, gt)
	if err != nil {
		c.Errorf("feather:newConfig err = %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	} else {
		//Create a TestRecord and save/cache it.
		tr := NewTestRecord(c, r, gt)
		tr.TestForm, err = GetTestFormat("Feather")
		if err != nil {
			c.Errorf("feather:GetTestFormat err = %v", err)
		}

		devKey := r.FormValue("devKey")
		if devKey != "" {
			fc["trackSetDevKey"] = fmt.Sprintf("'%s'", devKey)
			tr.DevKey = devKey
		} else {
			path := strings.Split(r.URL.Path, "/")
			if len(path) > 2 && path[2] != "" {
				fc["trackSetDevKey"] = fmt.Sprintf("'%s'", path[2])
				tr.DevKey = path[2]
			} else {
				fc["trackSetDevKey"] = "''"
			}
		}

		tr.Cache(c)

		h := HTMLHeader{
			Title: "FEATHER",
			ScriptList: []string{
				VersionFile("/", "strings-en-comp.js", c),
				VersionFile("/", "feather-comp.js", c)},
			JsConfig: fc,
		}
		p := &HTMLPage{
			Head:   h,
			OnLoad: "featherStart();",
		}
		err := staticSet.ExecuteTemplate(w, "page", p)
		if err != nil {
			c := appengine.NewContext(r)
			c.Errorf("feather:staticSet.ExecuteTemplate err = %v",
				err)
			http.Error(w, err.Error(),
				http.StatusInternalServerError)
			return
		}
	}
}

func egg(w http.ResponseWriter, r *http.Request) {
	gt := time.Now().Unix()
	c := appengine.NewContext(r)
	fc, err := newConfig(eggConfig, c, r, gt)
	if err != nil {
		c.Errorf("egg:newConfig err = %v", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	} else {
		//Create a TestRecord and save/cache it.
		tr := NewTestRecord(c, r, gt)
		tr.TestForm, err = GetTestFormat("Egg")
		if err != nil {
			c.Errorf("egg:GetTestFormat err = %v", err)
		}

		devKey := r.FormValue("devKey")
		if devKey != "" {
			fc["trackSetDevKey"] = fmt.Sprintf("'%s'", devKey)
			tr.DevKey = devKey
		} else {
			path := strings.Split(r.URL.Path, "/")
			if len(path) > 2 && path[2] != "" {
				fc["trackSetDevKey"] = fmt.Sprintf("'%s'", path[2])
				tr.DevKey = path[2]
			} else {
				fc["trackSetDevKey"] = "''"
			}
		}

		tr.Cache(c)

		h := HTMLHeader{
			Title: "EGG",
			ScriptList: []string{
				VersionFile("/", "strings-en-comp.js", c),
				VersionFile("/", "egg-comp.js", c)},
			StyleList: []string{VersionFile("/", "egg-comp.css",
				c)},
			JsConfig: fc,
		}
		p := &HTMLPage{
			Head:   h,
			OnLoad: "eggStart();",
		}
		err := staticSet.ExecuteTemplate(w, "page", p)
		if err != nil {
			c.Errorf("egg:staticSet.ExecuteTemplate err = %v", err)
			http.Error(w, err.Error(),
				http.StatusInternalServerError)
			return
		}
	}
}
